#-*-coding:utf-8-*-
from math import log
import Image
import datetime
'''
This module is used to code the image.
'''
class CodeCommon:
    '''
    the common module for the code and the decode.
    '''
    def __init__(self):
        pass

    def GenerateList(self,initvalue):
        '''
        Generate the List.
        '''       
        f = lambda x: (2.0 * x) / (1.0 - x ** 2)
        g = lambda y: ((complex(0,y) ** 2 + 1.0) / (2.0 * complex(0,y))).imag
        flag = 0
        outlist = list()
        while True:
            if flag % 2 == 0:
                initvalue = g(f(initvalue))
            else:
                initvalue = f(g(initvalue))
            flag += 1
            if initvalue >= 1:
                k = 1
            elif initvalue < 1 and initvalue > 0:
                k = 0
            elif initvalue > -1 and initvalue <= 0:
                k = 1
            else:
                k = 0
            yield k
        
    def NewGenerateKeys(self,initvalue,mode,extrdata,hw=[],bitlenth=8):
        '''
        the new function to generate the keys
        initvalue is the key seed
        mode is the function mode
            if mode == 1,to generate a width which will not bellow extrdata
            if mode == 2,to generate the width and height change keys.
            if mode == 3,to generate the key ,extrdata is the lenth.
        extrdata is decided by extradata.
        hw is the list which content the permutation key
        bitlenth is the bits which will be use.
        '''
        f = lambda x: (2.0 * x) / (1.0 - x ** 2)
        g = lambda y: ((complex(0,y) ** 2 + 1.0) / (2.0 * complex(0,y))).imag
        l = 0
        while True:
            x = '0b'
            for i in range(bitlenth):
                if i % 2 == 0:
                    initvalue = g(f(initvalue))
                else:
                    initvalue = f(g(initvalue))
                #initvalue = 2.0 * initvalue / (1 - initvalue ** 2)
                if initvalue >= 1:
                    k = 1
                elif initvalue < 1 and initvalue > 0:
                    k = 0
                elif initvalue > -1 and initvalue <= 0:
                    k = 1
                else:
                    k = 0
                x += str(k)     #get the keys.            
            l = int(x,2)
            if mode == 1:
                if l < extrdata:
                    continue
                else:
                    return l
            elif mode == 2:
                if len(hw[0]) < extrdata[0]:
                    if l < extrdata[0] and l not in hw[0]:
                        hw[0].append(l)
                else:
                    if len(hw[1]) < extrdata[1]:
                        if l < extrdata[1] and l not in hw[1]:
                            hw[1].append(l)
                    else:
                        return True
            elif mode == 3:
                if len(hw) < extrdata:
                    hw.append(l)
                else:
                    return True
            else:
                return False
                    
    def GetKeys(self,keyinit,nlent):
        '''
        get the 8bits keys.
        '''
        fid = self.GenerateList(keyinit)
        #fid = self.GenerateList(keyinit,8 * nlent)
        k = 0
        outlist = list()
        while k < nlent:
            x = '0b'
            for i in range(8):
                x += str(fid.next())
            #print x
            k += 1
            outlist.append(int(x,2))
        return outlist

    def GetRWidth(self,keyinit):
        '''
        Get the reconstructed width key.
        '''
        fid = self.GenerateList(keyinit)
        k = 0
        while not k:
            x = '0b'
            for i in range(8):
                x += str(fid.next())
            k = int(x,2)
            if k < 16:
                k = 0
        return k

    def forint(self,ins):
        '''
        up int
        ins must > 1
        '''
        if int(ins) == ins:
            return int(ins)
        else:
            return int(ins) + 1

    def GetPermutationList(self,info,initvalue):
        '''
        srclist is the points of the image
        info contains the height and the width of the image.
        initvalue refer to key
        '''
        height = info[0]
        hlist = list()
        width = info[1]
        wlist = list()
        F = max(info[0],info[1])
        a = self.forint(log(F) / log(2))        #
        fid = self.GenerateList(initvalue)
        hlist = self.GetPermutationNum(fid,height,a)
        wlist = self.GetPermutationNum(fid,width,a)
        return [hlist,wlist]

    def GetPermutationNum(self,fid,hn,a):
        '''
        fid is the generator
        hn is the limitation
        a is the bits.
        '''
        hlist = list()
        while len(hlist) < hn:
            x = '0b'
            for i in range(a):
                x += str(fid.next())
            k = int(x,2)
            if k < hn and k not in hlist:
                hlist.append(k)
        return hlist

    def GetStrSize(self,info):
        '''
        get the str size
        '''
        kout = ''
        kout += chr(info[0] / 256)
        kout += chr(info[0] % 256)
        kout += chr(info[1] / 256)
        kout += chr(info[1] % 256)
        return kout

    def GetInfoSize(self,ins):
        '''
        get the list size or the truple size.
        '''
        k1 = ord(ins[0]) * 256 + ord(ins[1])
        k2 = ord(ins[2]) * 256 + ord(ins[3])
        return (k1,k2)

    def StrToData(self,ins):
        '''
        Convert the string to imagedata
        '''
        lent = len(ins)
        return [(ord(ins[i]),ord(ins[i + 1]),ord(ins[i + 2])) for i in range(0,lent,3)]

    def DataToStr(self,ins):
        '''
        Convert the imagedata to string the oppsition procedure of the strToData.
        '''
        return reduce(lambda x,y:x + chr(y[0]) + chr(y[1]) + chr(y[2]),ins,'')

class CodeAlogrithm:
    '''
    the code alogrithm
    '''
    def __init__(self):
        '''
        initially
        '''
        self.codecom = CodeCommon()

    def Substitute(self,ins,keyinit):
        '''
        ins is the string
        keyinit is the key to the key order.
        '''
        #print ins
        lent = len(ins)
        #keys = self.codecom.GetKeys(keyinit,lent)       #get the keys
        keys = list()
        self.codecom.NewGenerateKeys(keyinit,3,lent,keys)
        #outlist = list()
        outstr = ''
        cr = 0
        for i in enumerate(ins,0):
            k = keys[i[0]]
            p = k ^ (((ord(i[1]) ^ k) + (255 - cr)) % 256)
            #outlist.append(p)
            outstr += chr(p)
            #cr = outlist[i[0]]
            cr = p
        #print outlist
        #return reduce(lambda x,y:x + chr(y),outlist,'')
        return outstr

    def Permute(self,imagelist,info,initvalue):
        '''
        imagelist is the datalist of the image
        info is the [height,width]
        initvalue
        '''
        #imagelist = self.GetMatrixList(imagelist,info)
        #hw = self.codecom.GetPermutationList(info,initvalue)
        hw = [[],[]]
        F = max(info[0],info[1])
        a = self.codecom.forint(log(F) / log(2))
        self.codecom.NewGenerateKeys(initvalue,2,info,hw,a)
        
        #print hw[0]
        imagelist = self.PermuteRows(imagelist,hw[0],info)
        #print imagelist
        #print hw[1]
        imagelist = self.PermuteCols(imagelist,hw[1],info)
        #print imagelist
        #return self.codecom.GetStrSize(info) + self.ToStr(imagelist)
        return self.ToStr(imagelist)

    def ToStr(self,imagelist):
        '''
        Get the string data.
        '''
        kout = ''
        for i in imagelist:
            for j in i:
                kout += chr(j)
        return kout

    def PermuteCols(self,imagelist,wlist,info):
        '''
        Permute the Cols
        '''
        kout = list()
        #print wlist
        for i in range(info[0]):
            for j in wlist:
                kout.append(imagelist[i * info[1] + j])
        return kout

    def PermuteRows(self,imagelist,hlist,info):
        '''
        Permute the Rows
        '''
        kout = list()
        #for i in hlist:
            #kout.append(imagelist[i])
        #return kout
        for i in hlist:
            k = i * info[1]
            for j in range(info[1]):
                kout.append(imagelist[k + j])
        return kout

    def GetMatrixList(self,imagelist,info):
        '''
        same with Permutation
        '''
        outlist = [[0 for col in range(info[1])]for row in range(info[0])]
        for i in enumerate(imagelist,0):
            outlist[i[0] / info[0]][i[0] % info[0]] = i[1]
        return outlist

    def testPer(self):
        '''
        test permutation
        '''
        c = Image.open('/home/hucheng/workspace/graduateproject/image-compress-encrypt/res/test.jpg')
        k = c.getdata()
        #print list(k)
        l = self.Permute(k,[5,5],2)
        #print len(l)
        #print l
        #return l
        avf = open('/home/hucheng/workspace/graduateproject/image-compress-encrypt/res/1.txt','wb')
        avf.write(l)
        avf.flush()
        avf.close()
        s = self.Substitute(l,2)
        print s
        return s

    def testP(self):
        k = self.GetPermutationList([1024,1024],2)
        print k

    def testlist(self):
        '''
        test GenerateList.
        '''
        fid = self.GenerateList(2)
        for i in range(100):
            print fid.next(),


class DecodingAlgorithm:
    '''
    Decode the coding
    '''
    def __init__(self):
        self.codecom = CodeCommon()   #the common module of the encoding and the decoding.

    def fromStr(self,ins):
        '''
        restruct the data by string.
        '''
        outk = list()
        klent = len(ins)
        for i in range(0,klent,3):
            outk.append((ord(ins[i]),ord(ins[i + 1]),ord(ins[i + 2])))
        return outk

    def Permutation(self,imagelist,info,initvalue):
        '''
        to restore the data
        '''
        #hw = self.codecom.GetPermutationList(info,initvalue)
        hw = [[],[]]
        F = max(info[0],info[1])
        a = self.codecom.forint(log(F) / log(2))
        self.codecom.NewGenerateKeys(initvalue,2,info,hw,a)
        imagelist = self.PermutationCols(imagelist,hw[1],info)
        #print imagelist
        imagelist = self.PermutationRows(imagelist,hw[0],info)
        #print imagelist
        return imagelist

    def PermutationCols(self,imagelist,wlist,info):
        '''
        restore the cols.
        '''
        kout = [0 for i in range(info[0] * info[1])]
        for i in range(info[0]):
            for j in enumerate(wlist,0):
                kout[i * info[1] + j[1]] = imagelist[i * info[1] + j[0]]
        return kout

    def PermutationRows(self,imagelist,hlist,info):
        '''
        restore the rows.
        '''
        kout = [0 for i in range(info[0] * info[1])]
        for i in enumerate(hlist,0):
            for j in range(info[1]):
                kout[i[1] * info[1] + j] = imagelist[i[0] * info[1] + j]
        return kout

    def Substitute(self,ins,keyinit):
        '''
        ins is the coded string,keyinit is the first views of the buffer.
        '''
        lent = len(ins)
        #keys = self.codecom.GetKeys(keyinit,lent)       #get the keys
        keys = list()
        self.codecom.NewGenerateKeys(keyinit,3,lent,keys)
        #outlist = list()
        outstr = ''
        cr = 0
        for i in enumerate(ins,0):
            k = keys[i[0]]
            p = k ^ (((ord(i[1]) ^ k) - (255 - cr)) % 256)
            #outlist.append(p)
            outstr += chr(p)
            cr = ord(ins[i[0]])
        #print outlist
        #return self.codecom.StrToData(reduce(lambda x,y:x + chr(y),outlist,''))
        #return reduce(lambda x,y:x + chr(y),outlist,'')
        return outstr

    def Permute(self,ins,info,initvalue):
        '''
        the main permute module.
        '''
        #psize = ins[0:4]
        #ins = ins[4:]
        imagelist = self.fromStr(ins)
        #info = self.codecom.GetInfoSize(psize)
        imagelist = self.Permutation(imagelist,info,initvalue)
        return imagelist

    def test(self,ins):
        c = self.Substitute(ins,2)
        avf = open('/home/hucheng/workspace/graduateproject/image-compress-encrypt/res/2.txt','wb')
        avf.write(c)
        avf.flush()
        avf.close()
        #self.Permute(ins,2)

if __name__ == '__main__':
    k = CodeAlogrithm()
    l = k.testPer()
    d = DecodingAlgorithm()
    d.test(l)


